using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.PageFlow;
using Microsoft.Practices.PageFlow.Services;
using WCSFContrib.PageFlow.Xml.Configuration;
using System.Globalization;

namespace WCSFContrib.PageFlow.Xml
{
    public class XmlPageFlowFactory : IPageFlowFactory,  IDisposable
    {
        private INavigationService _navigationService;
        private IDictionary<string, NavigationGraph> _navigationGraphs;
        private Dictionary<Type, XmlPageFlowDefinition> _xmlPageFlowDefinitionsCache = new Dictionary<Type, XmlPageFlowDefinition>();

        private object _readerLockerDefs = new object();
        private object _writerLockerDefs = new object();

        public XmlPageFlowFactory(IDictionary<string, NavigationGraph> navigationGraphs, INavigationService navigationService)
        {
            _navigationService = navigationService;
            _navigationGraphs = navigationGraphs;
        }

        public IPageFlow GetPageFlow(Type pageFlowType)
        {
            XmlPageFlowDefinition definition = GetOrCreateDefinition(pageFlowType);

            XmlPageFlowInstance instance = GetXmlPageFlowRuntime(definition.XmlPageFlowInstanceStore).CreatePageFlow(pageFlowType);

            return new XmlPageFlow(instance, _navigationService, definition);
        }

        public IPageFlow GetPageFlow(Type pageFlowType, Guid instanceId)
        {
            XmlPageFlowDefinition definition = GetOrCreateDefinition(pageFlowType);
            XmlPageFlowInstance instance = GetXmlPageFlowRuntime(definition.XmlPageFlowInstanceStore).GetPageFlow(instanceId);
            if (instance == null || instance.PageFlowType != pageFlowType)
            {
                throw new PageFlowException(String.Format(CultureInfo.CurrentCulture,
                    "The page flow definition {0} has been updated and the persisted page flow instance with the id {1} is not compatible. Please start a new page flow instance.",
                    pageFlowType.AssemblyQualifiedName, instanceId));
            }

            return new XmlPageFlow(instance, _navigationService, definition);
        }


        private XmlPageFlowDefinition GetOrCreateDefinition(Type pageFlowType)
        {
            XmlPageFlowDefinition cachedDefinition;

            lock (_readerLockerDefs)
            {
                if (!_xmlPageFlowDefinitionsCache.TryGetValue(pageFlowType, out cachedDefinition))
                {
                    lock (_writerLockerDefs)
                    {
                        if (!_xmlPageFlowDefinitionsCache.TryGetValue(pageFlowType, out cachedDefinition))
                        {
                            NavigationGraph navigationGraph = _navigationGraphs[pageFlowType.AssemblyQualifiedName];

                            cachedDefinition = new XmlPageFlowDefinition(navigationGraph);
                            _xmlPageFlowDefinitionsCache.Add(pageFlowType, cachedDefinition);
                        }
                    }
                }
            }

            return cachedDefinition;
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                foreach (XmlPageFlowRuntime runtime in _runtimes.Values)
                {
                    IDisposable disposableRuntime = runtime as IDisposable;
                    if (disposableRuntime != null )
                    {
                        disposableRuntime.Dispose();
                    }
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private object _writerLockerRuntimes = new object();

        private Dictionary<IXmlPageFlowInstanceStore, XmlPageFlowRuntime> _runtimes = new Dictionary<IXmlPageFlowInstanceStore, XmlPageFlowRuntime>();
        protected XmlPageFlowRuntime GetXmlPageFlowRuntime(IXmlPageFlowInstanceStore xmlPageFlowInstanceStore)
        {
            if (!_runtimes.ContainsKey(xmlPageFlowInstanceStore))
            {
                lock (_writerLockerRuntimes)
                {
                    if (!_runtimes.ContainsKey(xmlPageFlowInstanceStore))
                    {
                        _runtimes.Add(xmlPageFlowInstanceStore, new XmlPageFlowRuntime(xmlPageFlowInstanceStore));
                    }
                }
            }
            return _runtimes[xmlPageFlowInstanceStore];
        }

        //protected XmlPageFlowRuntimeBase XmlPageFlowRuntime
        //{
        //    get
        //    {
        //        if (_runtime == null)
        //        {
        //            //TODO: lock?
        //            //TODO: configuracion
        //            _runtime = new XmlPageFlowRuntime(new PageFlow.Xml.Session.XmlPageFlowInstanceSessionStore());
        //        }
        //        return _runtime;
        //    }
        //}
    }
}
